package com.cd.university.service.impl;

import com.cd.university.common.RestResult;
import com.cd.university.common.UniversityConstants;
import com.cd.university.common.utils.PageUtil;
import com.cd.university.common.utils.SpringUtil;
import com.cd.university.db.mapper.ScoreMapper;
import com.cd.university.db.pojo.ProjectFile;
import com.cd.university.db.pojo.Score;
import com.cd.university.db.pojo.ScoreKey;
import com.cd.university.service.AssessmentService;
import com.cd.university.vo.AssessmentVo;
import com.cd.university.vo.ProjectScoreVo;
import com.cd.university.vo.ProjectVo;
import com.cd.university.vo.ScoreVo;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 科研考核
 * @author 陈栋
 * @create 2021/11/11 18:28
 */
@Service
public class AssessmentServiceImpl implements AssessmentService, InitializingBean {

    @Autowired
    private ScoreMapper scoreMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private ProjectServiceImpl projectService;

    private boolean isModify = false;

    private ReentrantLock lock = new ReentrantLock();

    /**
     * achievement_type表中的atId对应的类
     */
    private static HashMap<Integer, String> achievementTypeMap = new HashMap();

    static {
        // 论文
        achievementTypeMap.put(1,"searchScoreByPaper");
        // 获奖
        achievementTypeMap.put(2,"searchScoreByAwarded");
        // 专利
        achievementTypeMap.put(3,"searchScoreByPatent");
        // 等级
        achievementTypeMap.put(4,"searchScoreByLevel");
    }

    /**
     * 存在Java缓存中的所有的考核方案
     */
    private List<ScoreVo> programList;

    private static List<Score> scores;

    @Override
    public void afterPropertiesSet() throws Exception {
        scores = scoreMapper.searchAll();
    }

    @Override
    public RestResult getAssessmentProgram(Integer pageNum, Integer pageSize) {
        checkCacheExists();
        PageInfo pageInfo = PageUtil.startPage(pageNum, pageSize, programList);
        return RestResult.success("所有的考核方案如下")
                .put("data",pageInfo);
    }

    @Override
    public RestResult getAllAssessmentProject(Integer pageNum, Integer pageSize) {
        ArrayList<ProjectScoreVo> projectScoreVos = new ArrayList<>();
        HashMap<Integer, ProjectScoreVo> map = getProjectScoreVoMapFromRedis();


        for (Map.Entry<Integer, ProjectScoreVo> voEntry : map.entrySet()) {
            projectScoreVos.add(voEntry.getValue());
        }

        // 分页
        PageInfo pageInfo = PageUtil.startPage(pageNum, pageSize, projectScoreVos);

        return RestResult.success("考核的项目如下")
                .put("data",pageInfo);
    }

    @Override
    public RestResult searchAssessmentProjectByProjectName(Integer pageNum,
                                                           Integer pageSize,
                                                           String projectName) {
        ArrayList<ProjectScoreVo> result = new ArrayList<>();
        HashMap<Integer, ProjectScoreVo> map = getProjectScoreVoMapFromRedis();
        for (Map.Entry<Integer, ProjectScoreVo> voEntry : map.entrySet()) {
            ProjectScoreVo value = voEntry.getValue();
            if (value.getProjectVo().getProjectName().contains(projectName)) {
                result.add(value);
            }
        }

        PageInfo pageInfo = PageUtil.startPage(pageNum, pageSize, result);
        return RestResult.success("搜索结果如下")
                .put("data",pageInfo);
    }

    @Override
    public RestResult searchAssessmentProjectByDepartmentId(Integer pageNum,
                                                            Integer pageSize,
                                                            Integer departmentId) {
        ArrayList<ProjectScoreVo> result = new ArrayList<>();
        HashMap<Integer, ProjectScoreVo> map = getProjectScoreVoMapFromRedis();
        for (Map.Entry<Integer, ProjectScoreVo> voEntry : map.entrySet()) {
            ProjectScoreVo value = voEntry.getValue();
            if (value.getProjectVo().getDepartmentId().equals(departmentId)) {
                result.add(value);
            }
        }

        PageInfo pageInfo = PageUtil.startPage(pageNum, pageSize, result);
        return RestResult.success("搜索结果如下")
                .put("data",pageInfo);
    }

    @Override
    public RestResult updateScores(List<Score> scoreList) {
        int row = 0;
        for (Score score : scoreList) {
            row += scoreMapper.updateByPrimaryKey(score);
        }

        AssessmentService bean = SpringUtil.getBean(AssessmentService.class);
        bean.loadToRedis();
        bean.checkCacheExists();
        isModify = true;
        return RestResult.success("成功修改了：" + row +  "条考核分数");
    }


    @Override
    @Async
    public void checkCacheExists() {
        if (!isModify && programList!=null) {
            return;
        }

        try {
            // 加锁更新，这里只需要有一个线程进行更新了就行，就能保证数据是最新的了
            if (lock.tryLock()) {

                // 创建一个
                programList = new ArrayList<>();

                // 得到数据库中所有的考核方案
                List<List<AssessmentVo>> lists = invokeMethod();
                for (List<AssessmentVo> list : lists) {
                    ScoreVo scoreVo = new ScoreVo();
                    scoreVo.setAtId(list.get(0).getAtId());
                    scoreVo.setAtName(list.get(0).getAtName());

                    // 添加其成果形式的所有具体考核
                    List<ScoreVo.Category> categories = new ArrayList<>();
                    for (AssessmentVo assessmentVo : list) {
                        ScoreVo.Category category = new ScoreVo.Category();
                        BeanUtils.copyProperties(assessmentVo, category);
                        categories.add(category);
                    }
                    scoreVo.setCategoryList(categories);

                    programList.add(scoreVo);
                }
            }
        }finally {
            lock.unlock();
        }
    }

    /**
     * 通过反射查询
     * @return
     */
    private List<List<AssessmentVo>> invokeMethod() {
        try {
            Class<? extends ScoreMapper> aClass = scoreMapper.getClass();
            List<List<AssessmentVo>> result = new ArrayList<>();
            // 循环遍历完所有的map中的value
            for (Map.Entry<Integer, String> entry : achievementTypeMap.entrySet()) {
                // 获得具体的方法
                Method method = aClass.getDeclaredMethod(entry.getValue());
                // 执行方法
                List<AssessmentVo> invoke = (List<AssessmentVo>) method.invoke(scoreMapper);

                // 添加到要返回的集合中
                result.add(invoke);
            }

            return result;
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 把所有的数据计算并且存放到Redis中
     */
    @Override
    @Async
    @Scheduled(fixedRate = 1000 * 60 * 30)
    public void loadToRedis() {

        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("---------------------------------延迟10秒之后执行的--------------------");
        try {
            // 尝试拿到锁，进行更新，如果拿不到锁，那么一定有线程抢到了锁，并进行更新
            if (lock.tryLock()) {

                // 从Redis中取出所有的项目Vo
                List<ProjectVo> projectVos = projectService.getProjectVosFromRedis();
                // 从Redis中去取出所有的项目文件对象
                List<ProjectFile> projectFiles = projectService.getProjectFilesFromRedis();

                scores = scoreMapper.searchAll();

                // 创建一个ProjectScoreVo的map对象，key为项目的id，value为ProjectScoreVo对象,并且存放在redis中
                HashMap<Integer, ProjectScoreVo> projectScoreVoMap = new HashMap<>();

                // 遍历所有已经结项的项目，这些项目是可以进行考核的
                for (ProjectVo projectVo : projectVos) {
                    // 当项目的状态为已经成功结项的时候
                    if (projectVo.getStatusId().equals(UniversityConstants.PROJECT_STATUS_FINALIZE_PROJECT)) {
                        // 查找该项目的所有的项目文件,并且计算项目文件所得的分数
                        Integer projectId = projectVo.getProjectId();

                        // 创建一个ProjectScoreVo对象，作为value
                        ProjectScoreVo projectScoreVo = getProjectScore(projectVo,projectFiles,projectId);
                        // 把项目信息放入到Vo对象中
                        projectScoreVo.setProjectVo(projectVo);

                        // 放入到projectScoreVoMap交给缓存
                        projectScoreVoMap.put(projectId,projectScoreVo);
                    }
                }

                // 放在Redis中
                ValueOperations opsForValue = redisTemplate.opsForValue();
                opsForValue.getAndSet("projectScoreVoMap",projectScoreVoMap);
            }

        }finally {
            lock.unlock();
        }

    }

    /**
     * 从redis中拿到项目的考核数据
     * @return 项目的考核数据，key为项目id，value为项目考核map
     */
    private HashMap<Integer, ProjectScoreVo> getProjectScoreVoMapFromRedis() {
        ValueOperations opsForValue = redisTemplate.opsForValue();
        HashMap<Integer, ProjectScoreVo> projectScoreVoMap = (HashMap<Integer, ProjectScoreVo>) opsForValue.get("projectScoreVoMap");

        if (projectScoreVoMap==null || projectScoreVoMap.size()==0) {
            loadToRedis();
        }
        return projectScoreVoMap;
    }

    /**
     * 获得当前项目的得分
     *
     * @param projectVo 项目信息
     * @param projectFiles 项目文件
     * @param projectId 项目id
     * @return map集合，key为的得分类型，value为值
     */
    private ProjectScoreVo getProjectScore(ProjectVo projectVo, List<ProjectFile> projectFiles,Integer projectId) {
        ProjectScoreVo projectScoreVo = new ProjectScoreVo();

        // 项目得分Map集合
        HashMap<String,Integer> projectScore = new HashMap<>();
        // 项目文件集合
        List<ProjectFile> projectFileList = new ArrayList<>();



        Integer awardedScore = 0;
        Integer paperScore = 0;
        Integer patentScore = 0;
        for (ProjectFile projectFile : projectFiles) {

            // 如果文件属于当前项目，那么计算当前文件的得分
            if (projectFile.getProjectId().equals(projectId) && !projectFile.getAtId().equals(0)) {
                Integer atId = projectFile.getAtId();
                Integer categoryId = projectFile.getCategoryId();

                projectFileList.add(projectFile);

                // 查看当前文件的得分

                // 如果是论文
                if (atId.equals(UniversityConstants.PROJECT_ACHIEVEMENT_PAPER)) {
                    paperScore = projectScore.get("paperScore");
                    paperScore= (paperScore==null?0:paperScore) + findScore(atId, categoryId);
                    projectScore.put("paperScore",paperScore);
                }
                // 获奖
                else if (atId.equals(UniversityConstants.PROJECT_ACHIEVEMENT_AWARD)) {
                    awardedScore = projectScore.get("awardedScore");
                    awardedScore = (awardedScore==null?0:awardedScore) + findScore(atId, categoryId);
                }
                // 专利
                else if (atId.equals(UniversityConstants.PROJECT_ACHIEVEMENT_PATENT)) {
                    patentScore = projectScore.get("patentScore");
                    patentScore = (patentScore==null?0:patentScore) + findScore(atId, categoryId);
                }
            }

            projectScore.put("paperScore",paperScore);
            projectScore.put("awardedScore",awardedScore);
            projectScore.put("patentScore",patentScore);
        }


        // 项目等级,直接计算项目的等级得分
        for (Score point : scores) {
            if (point.getAtId().equals(UniversityConstants.PROJECT_ACHIEVEMENT_LEVEL)
                    && point.getCategoryId().equals(projectVo.getLevelId())) {
                projectScore.put("levelScore", point.getScore());
                break;
            }
        }

        // 计算该项目的总得分
        Integer totalScore = 0;
        for (Map.Entry<String, Integer> entry : projectScore.entrySet()) {
            totalScore+=entry.getValue();
        }
        projectScore.put("totalScore",totalScore);

        // 把得分的map放入到Vo对象中
        projectScoreVo.setProjectScore(projectScore);
        // 把项目文件集合放入Vo对象中
        projectScoreVo.setProjectFileList(projectFileList);

        return projectScoreVo;
    }

    private int findScore(Integer atId, Integer categoryId) {
        Integer score = scoreMapper.selectByPrimaryKey(new ScoreKey(atId, categoryId)).getScore();
        if (score==null) {
            return 0;
        }
        return score;
    }
}
